package pso;

import java.io.IOException;
import java.util.ArrayList;

import run.FileWrite;

import function.Function;

public class PSO_Local {

	private Function f;
	private int psoSize;
	private int numberIterations;
	private ArrayList<Particle> population;

	private StringBuffer bfInterval = new StringBuffer();
	private StringBuffer bfGlobal   = new StringBuffer();
	
	private String expName;
	
	public PSO_Local(Function f, int psoSize, int numberIterations, String expName){
		this.f = f;
		this.psoSize = psoSize;
		this.numberIterations = numberIterations;
		this.expName = expName;		
	}


	public void run(){

		int iteration = 0;
		this.createPopulation();


		while(iteration < this.numberIterations){
			
			updatePersonalBestPosition();
			updateNeighborhoodPosition();
			updateVelocity(iteration);
			updatePosition();
			printBestPoint(iteration);
			writeInFile(this.bfInterval , this.expName);
			clearBuffers();
			iteration++;
		}
		
		this.bfGlobal.append(printBestPoint(iteration));
		this.bfGlobal.append(",");
		writeInFile(this.bfGlobal, this.expName + "-fitness");
		
	}

	//criando as populacoes
	private void createPopulation() {
		this.population = new ArrayList<Particle>();
		for (int i = 0; i < this.psoSize; i++) {
			population.add(new Particle(this.f,i));
		}
	}


	private void updateNeighborhoodPosition(){

		for (Particle p: this.population){
			int left  = 0;
			int right = 0;
			double vleft = 0d;
			double vright = 0d;
			double vcenter = 0d; 

			if( p.getIndex() == 0 ){
				left  = this.population.size() - 1;
				right = p.getIndex() + 1;
			}

			else if( p.getIndex() == this.population.size() - 1){
				left = p.getIndex() - 1;
				right = 0;
			}else{
				left  = p.getIndex() - 1;
				right = p.getIndex() + 1;
			}

			vcenter = p.getFitness();
			vleft   = this.population.get(left).getFitness();
			vright  = this.population.get(right).getFitness();

			if( vleft < vcenter && vleft <= vright)
			{
				p.setnBest(this.population.get(left).getPersonalBestPosition().clone());	
			}
			else if(vright < vcenter && vright < vleft){	
				p.setnBest(this.population.get(right).getPersonalBestPosition().clone());
			}
			else{
				p.setnBest(p.getPersonalBestPosition().clone());
			}
		}
	}


	//Retorna a melhor posi��o encontrada pelos os vizinhos de ordem 2 de uma part�cula
	private double[] getNeighborhoodBestPSO(Particle p){
		int left  = 0;
		int right = 0;
		double vleft = 0d;
		double vright = 0d;
		double vcenter = 0d; 

		if( p.getIndex() == 0 ){
			left  = this.population.size() - 1;
			right = p.getIndex() + 1;
		}

		else if( p.getIndex() == this.population.size() - 1){
			left = p.getIndex() - 1;
			right = 0;
		}else{
			left  = p.getIndex() - 1;
			right = p.getIndex() + 1;
		}

		vcenter = p.getFitness();
		vleft   = this.population.get(left).getFitness();
		vright  = this.population.get(right).getFitness();

		if( vleft < vcenter && vleft < vright)
		{
			return this.population.get(left).getPersonalBestPosition();
		}else if(vright < vcenter && vright < vleft){
			return this.population.get(right).getPersonalBestPosition();
		}			
		else 
			if(vcenter < vleft && vcenter < vright){
				return p.getPersonalBestPosition();
			}

		return p.getPersonalBestPosition();

	}

	private void updatePersonalBestPosition(){
		for (Particle particle : population) {
			particle.calculatePersonalBestPosition();
		}
	}

	private void updateVelocity(int iteration){
		for (Particle particle : population) {

			particle.calculateVelocity(   particle.getnBest() 
					,iteration, this.numberIterations);
		}
	}

	private void updatePosition(){
		for (Particle particle : population) {
			particle.calculatePosition();
		}
	}

	private double printBestPoint(int iteration) {
		double bestFitness = Double.MAX_VALUE;
		double fitnessParticula;
		for (Particle particle : this.population) {
			fitnessParticula = this.f.calculate(particle.getnBest());
			if (fitnessParticula < bestFitness){
				bestFitness = fitnessParticula;			
			}
		}
		
		if (iteration % ((this.numberIterations-1) / 10  ) == 0){
			bfInterval.append(bestFitness + "\t" + iteration/(double)100 + "\t" + this.expName);
			bfInterval.append("\n");
		}
		return bestFitness;
	}
	
	private void writeInFile(StringBuffer bf , String filename){
		try {
			FileWrite.writeInFile(bf.toString(),  "./resultadosPSO//" + filename);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void clearBuffers(){
		this.bfGlobal.delete(0, this.bfGlobal.length());
		this.bfInterval.delete(0, this.bfInterval.length());
	}
	
}
